﻿using Jint;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using T.FAS.Runtime.Base.Entity;
using T.FAS.Runtime.Expression.Entity;
using T.FAS.Runtime.Expression.Service;
using Jint.Native;
using System.Transactions;
using T.ServiceComponent.DataAccessEx.Core;
using System.Data.SqlClient;
using MySql.Data.MySqlClient;
using T.FAS.Runtime.Action.SPI;
using T.FAS.Runtime.Action.Service;
using Newtonsoft.Json.Linq;
using T.FAS.Runtime.Expression.SGPlugin;
using System.Dynamic;
using System.Threading;
using System.Linq;
using T.FAS.Runtime.Expression.Engine;
using System.Data;

namespace T.FAS.Runtime.Expression.UnitTest
{
    [TestClass]
    public class ExpressionUnit
    {
        #region JS
        /// <summary>
        /// ...
        /// </summary>
        [TestMethod]
        public void ExecuteArrTest()
        {
            string expr = @"var arr = ['1','2','3'];
            var brr = [];
            brr.push(...arr)
            return brr;";
            ExprResult exprResult = ExprService.GetInstance.Execute(expr, new FASContext(), new Dictionary<string, object>());
            Assert.IsTrue(exprResult.State == ResultState.Success);
        }
        /// <summary>
        /// 条件表达式
        /// </summary>
        [TestMethod]
        public void ExecuteDQExpr()
        {
            Dictionary<string, object> dic = new Dictionary<string, object>();
            dic.Add("filter", "1==3");
            ExprResult exprResult = ExprService.GetInstance.DQExecute("InputParam.filter != \"\" ?  InputParam.filter : \"1=1\"", dic);
            Assert.IsTrue(exprResult.State == ResultState.Success);
        }
        /// <summary>
        /// 表达式
        /// </summary>
        [TestMethod]
        public void ExecuteJSTest()
        {
            string expr = @"var cdo = {};
            cdo.Name = '123';
            cdo.Code = '12345'
            return cdo;";
            ExprResult exprResult = ExprService.GetInstance.Execute(expr, new FASContext(), new Dictionary<string, object>());
            Assert.IsTrue(exprResult.State == ResultState.Success);
        }
        #endregion
        #region C#
        /// <summary>
        /// Session
        /// </summary>
        [TestMethod]
        public void Session()
        {
            string expr = @"Session.UserId";
            ExprResult exprResult = ExprService.GetInstance.Execute(expr, new FASContext(), new Dictionary<string, object>());
            Assert.IsTrue(exprResult.State == ResultState.Success);
        }
        /// <summary>
        /// 表达式
        /// </summary>
        [TestMethod]
        public void ExecuteJS3()
        {
            ExprItem exprItem = new ExprItem
            {
                LastModifyTime = DateTime.Now,
                Code = "123"
            };
            Dictionary<string, object> dic = new Dictionary<string, object>();
            dic.Add("exprItem", exprItem);
            FASContext fASContext = new FASContext();
            string expr = @"exprItem.Code= 'asd';
            exprItem.LastModifyTime= '555'; ";
            ExprResult exprResult = ExprService.GetInstance.Execute(expr, fASContext, dic);
            Assert.IsTrue(exprResult.State == ResultState.Fail);
        }
        /// <summary>
        /// 表达式
        /// </summary>
        [TestMethod]
        public void ExecuteJSWithFASContext()
        {
            Dictionary<string, object> dic = new Dictionary<string, object>();
            dic.Add("Name", "123");
            dic.Add("Code", "456");
            FASContext fASContext = new FASContext();
            TRecord record = new TRecord(dic);
            fASContext.CurrentRecord = record;
            string expr = @"FASContext.CurrentRecord.Code= '444';
            FASContext.CurrentRecord.Name= '555'; ";
            ExprResult exprResult = ExprService.GetInstance.Execute(expr, fASContext, new Dictionary<string, object>());
            dic = JsonConvert.DeserializeObject<Dictionary<string, object>>(JsonConvert.SerializeObject(record));
            Assert.IsTrue(dic["Name"].ToString() == "555");
        }
        #endregion
        #region Promise
        /// <summary>
        /// Promise
        /// </summary>
        [TestMethod]
        public void ExecutePromiseJS()
        {
            var expr = @"new Promise((resolve, reject) => {
                var ret = Assert.IsTrue(1==1);
                if (ret.state == '1') {
                    resolve(ret.data);
                }
                else {
                    reject(ret.errmsg);
                }
            });";
            ExprResult exprResult = ExprService.GetInstance.Execute(expr, new FASContext(), new Dictionary<string, object>());
            Assert.IsNotNull(exprResult);
        }
        /// <summary>
        /// Promise
        /// </summary>
        [TestMethod]
        public void ExecutePromiseJS2()
        {
            var expr = @"var startUserId = '1';    
            var dataid = '2';
            var processInstanceName = '1提交的反馈单终止申请';
            var processDefinitionKey = 'AF_FeedOrderCloseFlow';
            var variables = { 'ProcessStartTime':Funcs.CurrDateTime()};

            AF.Submit(processDefinitionKey, variables, processInstanceName, dataid, startUserId).then(Funcs.Max(3,54));";
            ExprResult exprResult = ExprService.GetInstance.Execute(expr, new FASContext(), new Dictionary<string, object>());
            Assert.IsNotNull(exprResult);

            Action<JsValue> resolveFunc = null;
            Action<JsValue> rejectFunc = null;
            Jint.Engine engine = new Jint.Engine();
            engine.SetValue("f", new Func<JsValue>(() =>
            {
                var (promise, resolve, reject) = engine.Advanced.RegisterPromise();
                resolveFunc = resolve;
                rejectFunc = reject;
                return promise;
            }));

            engine.Execute("f();");
            resolveFunc(66);
        }
        #endregion
        #region 效率
        ///// <summary>
        ///// 表达式执行Jint效率测试
        ///// </summary>
        //[TestMethod]
        //public void JSEfficiencyTest1()
        //{
        //    Jint.Engine en = new Jint.Engine();
        //    DateTime start1 = DateTime.Now;
        //    for (int i = 0; i < 1000000; i++)
        //    {
        //        var s = en.Evaluate("1*2+3/4-(6+7)");
        //        Assert.IsTrue(s != null);
        //    }
        //    DateTime end1 = DateTime.Now;
        //    double totalSecond1 = Math.Round((end1 - start1).TotalSeconds, 2);
        //    Assert.IsTrue(totalSecond1 < 20);
        //}

        ///// <summary>
        ///// 表达式执行Jint效率测试
        ///// </summary>
        //[TestMethod]
        //public void JSEfficiencyTest2()
        //{
        //    Jint.Engine en = new Jint.Engine(options =>
        //    {
        //        options.LimitMemory(4_000_000);//限制内存
        //        options.TimeoutInterval(TimeSpan.FromSeconds(4));//超时时间
        //        options.MaxStatements(1000);
        //    });
        //    DateTime start1 = DateTime.Now;
        //    for (int i = 0; i < 1000000; i++)
        //    {
        //        var s = en.Evaluate("1*2+3/4-(6+7)");
        //        Assert.IsTrue(s != null);
        //    }
        //    DateTime end1 = DateTime.Now;
        //    double totalSecond1 = Math.Round((end1 - start1).TotalSeconds, 2);
        //    Assert.IsTrue(totalSecond1 < 20);
        //}
        ///// <summary>
        ///// 表达式执行Jint效率测试
        ///// </summary>
        //[TestMethod]
        //public void JSEfficiencyTest3()
        //{
        //    Jint.Engine en = new Jint.Engine();
        //    for (int i = 0; i < 10000; i++)
        //    {
        //        en.Evaluate(@"function add(a,b){
        //            return a+b;
        //        }
        //        add(" + i + "," + i + ")");
        //    }
        //}
        ///// <summary>
        ///// 表达式执行Jint效率测试
        ///// </summary>
        //[TestMethod]
        //public void JSEfficiency()
        //{
        //    DateTime dateTime = DateTime.Now;
        //    var result = "";
        //    for (var i = dateTime; i < dateTime.AddDays(10);)
        //    {
        //        i = i.AddMinutes(1);
        //        result += i.ToString("yyyy-MM-dd HH:mm:ss.fff");
        //    }
        //    var s = "wo,yao,qu,chong,dian";
        //    var arr = s.Split(',');
        //    for (var i = 0; i < arr.Length; i++)
        //    {
        //        for (int j = 0; j < 100; j++)
        //        {
        //            result += arr[i] + j + (1 * 2 + 3.0 / 4 - 5 + 6);
        //        }
        //    }
        //    Assert.IsNotNull(result);
        //}
        ///// <summary>
        ///// 表达式执行Jint效率测试
        ///// </summary>
        //[TestMethod]
        //public void ExecuteEfficiencySG()
        //{
        //    int sum = 0;
        //    for (int i = 0; i < 1000000; i++)
        //    {
        //        sum++;
        //    }
        //    Assert.IsNotNull(sum);
        //}
        ///// <summary>
        ///// 表达式执行Jint效率测试
        ///// </summary>
        //[TestMethod]
        //public void ExecuteEfficiencyJS()
        //{
        //    Dictionary<string, object> dic = new Dictionary<string, object>();
        //    var expr = @"var sum = 0;
        //    for (var i = 0; i < 1000000; i++)
        //    {
        //        sum++;
        //    }";
        //    ExprResult exprResult = ExprService.GetInstance.JSExecute(expr, dic);
        //    Assert.IsTrue(exprResult.State == ResultState.Success);
        //}
        ///// <summary>
        ///// 表达式执行Jint效率测试
        ///// </summary>
        //[TestMethod]
        //public void ExecuteEfficiencySG2()
        //{
        //    int sum = 0;
        //    for (int i = 0; i < 100000; i++)
        //    {
        //        add(i,i);
        //    }
        //    Assert.IsNotNull(sum);
        //}
        //public int add(int x,int y)
        //{
        //    return x + y;
        //}
        ///// <summary>
        ///// 表达式执行Jint效率测试
        ///// </summary>
        //[TestMethod]
        //public void ExecuteEfficiencyJS2()
        //{
        //    Jint.Engine en = new Jint.Engine();
        //    en.Execute(@"function add(a,b){
        //            return a+b;
        //        }");
        //    for (int i = 0; i < 100000; i++)
        //    {
        //        en.Evaluate(@"add(" + i + "," + i + ")");
        //    }
        //}
        ///// <summary>
        ///// 表达式执行Jint效率测试
        ///// </summary>
        //[TestMethod]
        //public void ExecuteJS()
        //{
        //    Dictionary<string, object> dic = new Dictionary<string, object>();
        //    dic.Add("expressionUnit", new ExpressionUnit());
        //    var expr = @"expressionUnit.ExecuteEfficiencySG()";
        //    ExprResult exprResult = ExprService.GetInstance.JSExecute(expr, dic);
        //    Assert.IsTrue(exprResult.State == ResultState.Success);
        //}
        #endregion
        /// <summary>
        /// 注释执行
        /// </summary>
        [TestMethod]
        public void ExecuteAnnotation()
        {
            Dictionary<string, object> dic = new Dictionary<string, object>();
            var expr = @"//测试";
            ExprResult exprResult = ExprService.GetInstance.JSExecute(expr, dic);
            Assert.IsTrue(exprResult.State == ResultState.Success);
        }
        /// <summary>
        /// 注释测试
        /// </summary>
        [TestMethod]
        public void Annotation()
        {
            Dictionary<string, object> dic = new Dictionary<string, object>();
            var expr = @"//测试 测试测试
            return 'ces ';";
            ExprResult exprResult = ExprService.GetInstance.JSExecute(expr, dic);
            Assert.IsTrue(exprResult.State == ResultState.Success);
        }
        #region try catch
        /// <summary>
        /// 表达式执行
        /// </summary>
        [TestMethod]
        public void ExecuteTryJS()
        {
            Dictionary<string, object> dic = new Dictionary<string, object>();
            var expr = @"
            try {
               JSON.parse('[3]2');
            }
            catch(ex) {
                Log.Log(ex);
            }
            finally
            {
                Log.Log('finally');
            }";
            ExprResult exprResult = ExprService.GetInstance.JSExecute(expr, dic);
            Assert.IsTrue(exprResult.State == ResultState.Success);
        }
        /// <summary>
        /// Throw
        /// </summary>
        [TestMethod]
        public void Throw()
        {
            Dictionary<string, object> dic = new Dictionary<string, object>();
            dic.Add("ID", "1234");
            dic.Add("Code", "1");
            dic.Add("Name", "2");
            TRecord record = new TRecord(dic);
            record.ChangeSet = new TChangeSet();
            Dictionary<string, object> inputParam = new Dictionary<string, object>();
            inputParam.Add("record", record);
            ExprResult exprResult = ExprService.GetInstance.JSExecute("record.Name='231';\n record.a = '44444'; throw '2'; \n  return record;", inputParam);
            Assert.IsTrue(exprResult.RowNum == 2);
        }
        /// <summary>
        /// 表达式执行
        /// </summary>
        [TestMethod]
        public void ExecuteTryCSharp()
        {
            Dictionary<string, object> dic = new Dictionary<string, object>();
            dic.Add("test", new ExpressionUnit());
            var expr = @"
            try {
               test.Test();
            }
            catch(ex) {
                Log.Log(ex);
            }
            finally
            {
                Log.Log('finally');
            }";
            ExprResult exprResult = ExprService.GetInstance.JSExecute(expr, dic);
            Assert.IsTrue(exprResult.State == ResultState.Success);
        }
        #endregion

        /// <summary>
        /// 表达式执行
        /// </summary>
        [TestMethod]
        public void ExecuteCSharp()
        {
            Dictionary<string, object> dic = new Dictionary<string, object>();
            dic.Add("ID", "1234");
            dic.Add("Code", "1");
            dic.Add("Name", "2");
            TRecord record = new TRecord(dic);
            record.ChangeSet = new TChangeSet();
            bool s = record.ChangeSet.IsContain("Name");
            Dictionary<string, object> inputParam = new Dictionary<string, object>();
            inputParam.Add("record", record);
            ExprResult exprResult = ExprService.GetInstance.JSExecute("record.ChangeSet.IsContain('Name')", inputParam);
            Assert.IsTrue(exprResult.State == ResultState.Success);
        }
        /// <summary>
        /// 表达式执行
        /// </summary>
        [TestMethod]
        public void ExecuteRecord()
        {
            Dictionary<string, object> dic = new Dictionary<string, object>();
            dic.Add("ID", "1234");
            dic.Add("Code", "1");
            dic.Add("Name", "2");
            TRecord record = new TRecord(dic);
            Dictionary<string, object> inputParam = new Dictionary<string, object>();
            inputParam.Add("record", record);
            ExprResult exprResult = ExprService.GetInstance.JSExecute("Funcs.SerializeObject(record)", inputParam);
            Assert.IsTrue(exprResult.State == ResultState.Success);
        }
        /// <summary>
        /// 表达式执行
        /// </summary>
        [TestMethod]
        public void Execute()
        {
            try
            {
                FASRExprService fASRExprService = new FASRExprService();
                Dictionary<string, object> dic = new Dictionary<string, object>();
                dic.Add("bpoI1", "1");
                dic.Add("opCode", "2");
                object obj = fASRExprService.ExecutePlugin("HasOP", dic);
                Assert.IsNotNull(obj);
            }
            catch (Exception e)
            {
                Assert.IsNotNull(e);
            }
        }
        /// <summary>
        /// 表达式执行
        /// </summary>
        [TestMethod]
        public void Execute3()
        {
            dynamic d = new ExpandoObject();
            d.a = "22";
            Assert.IsNotNull(d);
        }
        /// <summary>
        /// 表达式执行
        /// </summary>
        [TestMethod]
        public void Execute2()
        {
            Dictionary<string, object> dic = new Dictionary<string, object>();
            dic.Add("ID", "1234");
            dic.Add("Code", "1");
            dic.Add("Name", "2");
            TRecord record = new TRecord(dic);
            record.ChangeSet = new TChangeSet();
            Dictionary<string, object> inputParam = new Dictionary<string, object>();
            inputParam.Add("record", record);
            ExprResult exprResult = ExprService.GetInstance.JSExecute("record.Name='231'; record.a = '44444';  return record;", inputParam);
            Assert.IsTrue(exprResult.State == ResultState.Success);
        }
        /// <summary>
        /// 表达式执行
        /// </summary>
        [TestMethod]
        public void Execute4()
        {
            Dictionary<string, object> dic1 = new Dictionary<string, object>();
            dic1.Add("ID", "1234");
            dic1.Add("Code", "1");
            dic1.Add("Name", "2");
            TRecord record1 = new TRecord(dic1);

            Dictionary<string, object> dic2 = new Dictionary<string, object>();
            dic2.Add("ID", "123");
            dic2.Add("Code", "1");
            dic2.Add("Name", "2");
            TRecord record2 = new TRecord(dic2);

            List<TRecord> records = new List<TRecord>();
            records.Add(record1);
            records.Add(record2);
            Dictionary<string, object> inputParam = new Dictionary<string, object>();
            inputParam.Add("records", records);
            ExprResult exprResult = ExprService.GetInstance.JSExecute("records.Find(t=>t.ID == '123')", inputParam);
            Assert.IsTrue(exprResult.State == ResultState.Success);
        }
        /// <summary>
        /// 表达式执行
        /// </summary>
        [TestMethod]
        public void Execute5()
        {
            string expr = @"var s1 = { ID:'123',Name: '1' },s2 = { ID:'1234',Name: '1' };
                            var list = [];list.push(s1);list.push(s2);
                            list.filter(t=>t.ID == '123');";
            Dictionary<string, object> inputParam = new Dictionary<string, object>();
            ExprResult exprResult = ExprService.GetInstance.JSExecute(expr, inputParam);
            Assert.IsTrue(exprResult.State == ResultState.Success);
        }
        /// <summary>
        /// 表达式执行
        /// </summary>
        [TestMethod]
        public void ExecuteArray()
        {
            string expr = @"var list = ['1','2','3'];
                            list.filter(t=>t == '1');";
            Dictionary<string, object> inputParam = new Dictionary<string, object>();
            ExprResult exprResult = ExprService.GetInstance.JSExecute(expr, inputParam);
            Assert.IsTrue(exprResult.State == ResultState.Success);
        }
        /// <summary>
        /// 表达式执行
        /// </summary>
        [TestMethod]
        public void ExecuteList()
        {
            List<string> list = new List<string>() {"1","2","3" };
            Dictionary<string, object> inputParam = new Dictionary<string, object>();
            inputParam.Add("list",list);
            var s = list.Where(t => t == "1");
            var s2 = list.FindAll(t => t == "1");
            list.Select(t => t == "1");

            //string expr1 = "return list.filter(t=>t == '1');";
            //string expr1 = "return list.Add('4');";
            string expr1 = "return list.FindAll(t=>t == '1');";
            ExprResult exprResult = ExprService.GetInstance.JSExecute(expr1, inputParam);
            Assert.IsTrue(exprResult.State == ResultState.Success);
        }
        /// <summary>
        /// 表达式执行
        /// </summary>
        [TestMethod]
        public void Execute33()
        {
            Dictionary<string, object> inputParam = new Dictionary<string, object>();
            string expr1 = "Number(Math.random().toString().substr(2,3) + Date.now()).toString(36);";
            ExprResult exprResult = ExprService.GetInstance.JSExecute(expr1, inputParam);
            Assert.IsTrue(exprResult.State == ResultState.Success);
        }
        /// <summary>
        /// ExecuteFunc
        /// </summary>
        [TestMethod]
        public void ExecuteFunc()
        {
            string expressionStr = "1 + 2 + b.Execute(3.6, 4.1)";
            var input = new Dictionary<string, object>();
            input.Add("b", new FuncA { value = 23 });
            var result = ExprClient.GetInstance.JSExecute(expressionStr, input);
            Assert.IsTrue(result.State == ResultState.Success && Convert.ToString(result.Result) == "33.7");
        }
        /// <summary>
        /// 校验
        /// </summary>
        [TestMethod]
        public void ExecuteBool()
        {
            Order order =  new Order { DateTime = DateTime.Now, OrderCount = 1  };
            DataTable dt = new DataTable();
            string expressionStr = "order.OrderCount > 0 ";
            var input = new Dictionary<string, object>();
            input.Add("order", order);
            var result = ExprClient.GetInstance.JSExecute(expressionStr, input);
            Assert.IsTrue(result.State == ResultState.Success);
        }
    }
    public class FuncA
    {
        public int value { get; set; }
        public double Execute(double x, double y)
        {
            return x + y + value;
        }
    }
    public class Order
    {
        public DateTime DateTime { get; set; }

        public int OrderCount { get; set; }
        public bool IsTrue { get; set; }
    }
}
